using System;
using System.Threading;

namespace Threading
{
    public class WorkItemRunner<TWorkItemState>
    {
        public ManualResetEvent WorkDoneEvent = new ManualResetEvent(false);    
    
        public WorkItemRunner(WorkItem<TWorkItemState> workItem)
        {
            WorkItem = workItem;
        }

        public WorkItem<TWorkItemState> WorkItem
        {
            get; set;
        }
        
        private Thread WorkerThread
        {
            get;
            set;
        }

        public void Run()
        {
            this.WorkerThread = new Thread(this.StartWorking);
            this.WorkerThread.Start(this.WorkItem.State);
        }
        
        public void Abort()
        {
            if (this.WorkerThread != null)
            {
                this.WorkItem.Cancel();
                this.WorkerThread.Abort();
            }
        }

        private void StartWorking(object state)
        {
            this.WorkDoneEvent.Reset();
            this.WorkItem.IsFinished = false;
            this.WorkItem.IsStarted = true;
            
            this.WorkItem.LastException = null;
            this.WorkItem.ThereWereErrors = false;

            try
            {
                if (!this.WorkItem.IsCanceled)
                {
                    this.WorkItem.WorkMethod(this.WorkItem.State);
                }
            }
            catch (Exception ex)
            {
                this.WorkItem.ThereWereErrors = true;
                this.WorkItem.LastException = ex;
            }
            finally
            {
                this.SignalThatWorkIsDone();
                this.WorkItem.IsFinished = true;
            }
        }

        private void SignalThatWorkIsDone()
        {
            this.WorkDoneEvent.Set();
        }
    }
}